package source;

import java.awt.*;
import source.view.*;

public class Map {

	/**
	 * @author Kyle Savoy
	 * 
	 * Attributes:
	 * 		Tile[][] square
	 * 
	 * Properties:
	 * 		height
	 * 		width
	 * 
	 * Constructors:
	 * 		default
	 * 		Map(square: Tile[][]) - assumes 0 origin at SW
	 * 
	 * initialization methods:
	 * 		setSquare(square: Tile[][])
	 * 
	 * accessor methods:
	 * 		getSquare(): Tile[][]
	 * 		getDimension(): Dimension - returns length and width in Dimension form
	 * 
	 * public functions:
	 * 		move(currentUnit: Unit, direction: int) - moves the given unit in the specified direction
	 * 		move(currentUnit: Unit, toTile: Tile) - queues up moves to a given tile
	 * 		attack(attacker: Unit, toTile: Tile) - forces the given unit to attack anything on the provided Tile
	 * 		getMovements(query: Unit): Tile[] - returns an array of tiles that the given unit can move to
	 * 
	 * @param args
	 */
	
	private Tile[][] square;
	private int height,
				width;
			
	public Map(){
		square=new Tile[0][0];
		height=0;
		width=0;
	}
	
	public Map(Tile[][] square){
		this.square=square;
		height=square[0].length;
		width=square.length;
	}
	
	public Map(int height, int width){
		this.height=height;
		this.width=width;
		square=new Tile[width][height];
		for(int i=0; i<width; i++){
			for(int j=0; j<height; j++){
				square[i][j]=generateTile(i, j);
			}
		}
	}
	
	private Tile generateTile(int x, int y){
		return new Tile(x, y, TerrainFactory.createTerrain(Math.random()));
	}
	
	public void setSquare(Tile[][] square){
		this.square=square;
		height=square[0].length;
		width=square.length;
	}
	
	public Tile getSquare(int x, int y){
		return square[x][y];
	}
	
	public Tile[][] getSquare(){
		return square;
	}
	
	public Tile getRandomStartingTile(){
		int randomX, randomY;
		java.util.Random rand = new java.util.Random();
		do{
			randomX=rand.nextInt(width);
			randomY=rand.nextInt(height);
			if(square[randomX][randomY].getTerrain().isPassable() && square[randomX][randomY].getTheItem()==null)
				return square[randomX][randomY];
		}while(true);
	}
	
	public Dimension getDimension(){
		return new Dimension(width, height);
	}
	
	/*
	 1	=	SW
	 2	=	S
	 3	=	SE
	 4	=	W
	 5	=	null
	 6	=	E
	 7	=	NW
	 8	=	N
	 9	=	NE
	 */
	
	public boolean move(Unit currentUnit, int direction){//moves the currentUnit in the int direction recovered from the Controller
		if (currentUnit.getRemainingMoves() <= 0) {
			MainScreen.writeToConsole("Map: Selected Unit cannot move anymore this turn", new Color(255, 85, 0));
			return false;
		}
		Tile currentTile=currentUnit.getPosition(),
				newTile=null;
		int currentX=currentTile.getX(),
			currentY=currentTile.getY();
		switch(direction){
		case 1://SW
			if(currentX-1<0 || currentY+1>height)
				break;
			newTile=square[currentX-1][currentY+1];
			break;
		case 2://S
			if(currentY+1>height)
				break;
			newTile=square[currentX][currentY+1];
			break;
		case 3://SE
			if(currentX+1>width || currentY+1>height)
				break;
			newTile=square[currentX+1][currentY+1];
			break;
		case 4://W
			if(currentX-1<0)
				break;
			newTile=square[currentX-1][currentY];
			break;
		case 6://E
			if(currentX+1>width)
				break;
			newTile=square[currentX+1][currentY];
			break;
		case 7://NW
			if(currentX-1<0 || currentY-1<0)
				break;
			newTile=square[currentX-1][currentY-1];
			break;
		case 8://N
			if(currentY-1<0)
				break;
			newTile=square[currentX][currentY-1];
			break;
		case 9://NE
			if(currentX+1>width || currentY-1<0)
				break;
			newTile=square[currentX+1][currentY-1];
			break;
		default: return false;
		}
		int PID = newTile.getUnitPlayerID();
		if (PID == -1 || PID == currentUnit.getPlayer().getID()) {
			Terrain newTileTerrain=newTile.getTerrain();
			if(!newTileTerrain.isPassable())
			{
				MainScreen.writeToConsole("Map:  Unit cannot move into impassable terrain", new Color(255, 85, 0));
				return false;
			}
			Item item=newTile.getTheItem();
			if(item != null && item.getIsPassable()){
				item.takeAction(currentUnit.getPlayer());
				newTile.removeItem();
			}
			if(item != null && !item.getIsPassable()){
				MainScreen.writeToConsole("Map: Unit cannot move over an obstacle", new Color(255, 85, 0));
				return false;
			}
			AreaEffect ae=newTile.getAreaEffect();
			if(ae != null)
				if(ae.takeAction(currentUnit))
					return false;
			currentTile.removeUnit(currentUnit);
			newTile.addUnit(currentUnit);
			currentUnit.setPosition(newTile);
			currentUnit.getPlayer().modifyExplored(currentUnit);
			currentUnit.decrementMoves(newTileTerrain.getMovementCost());
			MainScreen.writeToConsole("Map: Unit has moved to X: " + newTile.getX() + " : Y: " + newTile.getY(), new Color(255, 85, 0));	
			return true;
		}
		return false;
	}//assumes 0 origin at SW
	
	public boolean move(Unit currentUnit, Tile toTile){//returns true if the unit on average moved towards the goal
		int fromTileX,
			fromTileY,
			toTileX=toTile.getX(),
			toTileY=toTile.getY();
		int xDiff,
			yDiff;
		Tile start=currentUnit.getPosition();
		while(currentUnit.getRemainingMoves() > 0 && !currentUnit.checkDestination()){
			fromTileX=currentUnit.getPosition().getX();
			fromTileY=currentUnit.getPosition().getY();
			
			xDiff=fromTileX-toTileX;
			yDiff=toTileY-fromTileY;
			if(xDiff<0 && yDiff<0) {//currentUnit is NW of desired tile
				if(move(currentUnit, 9)){//move NE to compensate
					continue;
				}
			}
			if(xDiff>0 && yDiff<0) {//currentUnit is SE of desired tile
				if(move(currentUnit, 7)){//move NW to compensate
					continue;
				}
			}
			if(xDiff<0 && yDiff>0) {//currentUnit is NW of desired tile
				if(move(currentUnit, 3)){//move SE to compensate
					continue;
				}
			}
			if(xDiff>0 && yDiff>0) {//currentUnit is NE of desired tile
				if(move(currentUnit, 1)){//move SW to compensate
					continue;
				}
			}
			if(xDiff<0) {//currentUnit is W of desired tile
				if(move(currentUnit, 6)){//move E to compensate
					continue;
				}
			}
			if(xDiff>0) {//currentUnit is E of desired tile
				if(move(currentUnit, 4)){//move W to compensate
					continue;
				}
			}
			if(yDiff<0) {//currentUnit is S of desired tile
				if(move(currentUnit, 8)){//move N to compensate
					continue;
				}
			}
			if(yDiff>0) {//currentUnit is N of desired tile
				if(move(currentUnit, 2)){//move S to compensate
					continue;
				}
			}
			if(currentUnit.checkDestination())
			{
				MainScreen.writeToConsole("Mission Complete");
				return true;
			}
		}
		if(currentUnit.checkDestination())
		{
			MainScreen.writeToConsole("Mission Complete");
			return true;
		}
		return squaresAway(start, toTile)<squaresAway(currentUnit.getPosition(), toTile);
	}
	//untested... method should work for all unobstructed paths, so test that first. Obstructed pathfinding should be implemented here later
	
	/*public String attack(Unit attacker, Tile toTile){//attacker attacks anything on the passed tile and returns a string representation of the attack
		if(!(attacker instanceof MeleeUnit && squaresAway(attacker.getPosition(), toTile)==1))//its a melee unit and not adjacent
			return "The enemy has to be adjacent for " + attacker + " to attack.";
		else if(!(attacker instanceof RangedUnit && squaresAway(attacker.getPosition(), toTile)>attacker.getMaxRange()))//it's a ranged unit and trying to attack outside its max range
			return "The attack was outside of " + attacker + "\'s range.";
		else if(!(attacker instanceof RangedUnit && squaresAway(attacker.getPosition(), toTile)<attacker.getMinRange()))//it's a ranged unit trying to attack inside its min range
			return "The attack was inside of " + attacker "\'s range.";
		String response=new String();
		Unit defender=toTile.getTheUnit();//Tile currently returns a Unit[], check to see if necessary
		defender.setHealth(defender.getHealth()-defender.getArmor()-attacker.getAttackPower());//attacker damages enemy
		response=(attacker + " has attacked " + defender + " and done " + attacker.getOffDamage() + " damage.");
		if(attacker instanceof MeleeUnit && squaresAway(attacker.position(), defender.position())==1)//counterattack
			attacker.setHealth(attacker.getHealth()-attacker.getArmor()-defender.getDefensivePower());
		if(defender.getHealth()<=0){//check if defender died
			toTile.remove(defender);//no remove(unit: Unit) method in Tile
			response.concat("\n" + defender + "has been destroyed.");
		}
		if(attacker.getHealth()<=0){//check if attacker died during counterattack
			attacker.getPosition().remove(attacker);//no remove(unit: Unit) method in Tile
			response.concat("\n" + attacker + " has been destroyed.")
		}
		return response;
	}*/
	
	private int squaresAway(Tile tile1, Tile tile2){
		int tile1X=tile1.getX(),
			tile1Y=tile1.getY(),
			tile2X=tile2.getX(),
			tile2Y=tile2.getY();
		int xDiff=Math.abs(tile1X-tile2X),
			yDiff=Math.abs(tile1Y-tile2Y);
		return xDiff > yDiff ? xDiff : yDiff;
	}
	
	/*public Tile[] getMovements(Unit query){//returns an array of tiles that the unit can move to
		int numberOfMoves=query.getMovementRate();
		Tile currentTile=query.getPosition();
		int currentX=currentTile.getXCoordinate(),//no getXCoordinate method
			currentY=currentTile.getYCoordinate();//no getYCoordinate method
		Tile[] possibleMoves=new Tile[numberOfMoves*numberOfMoves];
		if(currentX-1>0 && currentY-1>0 && square[currentX-1][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //SW
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //S
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentX+1<width && currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //SE
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //W
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //E
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //NW
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //N
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
		
		if(currentY-1>0 && square[currentX][currentY-1].canSupportUnit())//no canSupportUnit method in Tile //NE
			possibleMoves=getMovements(possibleMoves, square[currentX-1][currentY-1], numberOfMoves-1, currentTile);
	}*/
	
	private Tile[] getMovements(Tile[] possibleMoves, Tile currentTile, int numberOfMoves, Tile previousTile){
		return new Tile[]{};
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.print("hello");
	}
	
	public String toString(){
		String string=new String("");
		for(int j=0; j<square.length; j++){
			string.concat("[");
			for(int i=0; i<square[j].length; i++){
				string.concat(square[i][j] + ", ");
			}
			string.concat("]");
		}
		string.concat("\n\nDimensions: " + width + " x " + height);
		return string;
	}

}
